package com.eugene.element;

import java.io.Serializable;
import java.util.List;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.eugene.entity.MvData;
import com.eugene.quant.Derivative;
import com.eugene.quant.distribution.CumulativeNormalDistribution;
import com.eugene.quant.distribution.NormalDistribution;
import com.eugene.quant.solver.Newton;

public class Option implements Derivative ,Serializable{
	private final static Logger logger = LoggerFactory.getLogger(Option.class);
	
	private FnCalendar baseDate;
	private String prodId;
	private String currency ;
	private boolean isCall;
	private boolean call;
	private String underlyingId;
	private double underlyingPrice;
	private double strikePrice;
	private FnCalendar maturityDate;
	private double hisVol;
	private double multiplier;
	private String ircId;
	private double irRate;
	private double currentPrice;
	private double volSpread;
	private List<MvData> prodUnderPrices;
	private List<MvData> rfUnderPrices;
	private List<MvData> currPrices;
	
	

	public List<MvData> getProdUnderPrices() {
		return prodUnderPrices;
	}
	public void setProdUnderPrices(List<MvData> prodUnderPrices) {
		this.prodUnderPrices = prodUnderPrices;
	}
	public List<MvData> getRfUnderPrices() {
		return rfUnderPrices;
	}
	public void setRfUnderPrices(List<MvData> rfUnderPrices) {
		this.rfUnderPrices = rfUnderPrices;
	}
	public List<MvData> getCurrPrices() {
		return currPrices;
	}
	public void setCurrPrices(List<MvData> currPrices) {
		this.currPrices = currPrices;
	}
	public Option(){
	}
	public Option(String optionId){
		this.prodId = optionId;
	}

	public Option(FnCalendar baseDate, String prodId, String currency,boolean isCall
			, String underlyingId, double underPrice,double strikePrice
			, FnCalendar matDate, double multiplier, String ircId, double hisVol
			, double irRate, double currPrice){
		this.baseDate = baseDate;
		this.prodId = prodId;
		this.currency = currency;
		this.isCall = isCall;
		this.underlyingId = underlyingId;
		this.underlyingPrice = underPrice;
		this.strikePrice = strikePrice;
		this.maturityDate = matDate;
		this.multiplier = multiplier;
		this.hisVol	= hisVol;
		this.irRate = irRate;
		this.currentPrice = currPrice;
		this.volSpread =0.0;
	}


	public boolean isCall() {
		return call;
	}
	public void setCall(boolean call) {
		this.call = call;
	}
	public FnCalendar getBaseDate() {
		return baseDate;
	}

	public void setBaseDate(FnCalendar baseDate) {
		this.baseDate = baseDate;
	}

	public String getProdId() {
		return prodId;
	}

	public void setProdId(String prodId) {
		this.prodId = prodId;
	}
	
	public String getCurrency() {
		return currency;
	}

	public void setCurrency(String currency) {
		this.currency = currency;
	}



	public boolean getIsCall() {
		return isCall;
	}

	public void setIsCall(boolean isCall) {
		this.isCall = isCall;
	}

	public String getUnderlyingId() {
		return underlyingId;
	}

	public void setUnderlyingId(String underlyingId) {
		this.underlyingId = underlyingId;
	}

	public double getUnderlyingPrice() {
		return underlyingPrice;
	}

	public void setUnderlyingPrice(double underlyingPrice) {
		this.underlyingPrice = underlyingPrice;
	}

	public double getStrikePrice() {
		return strikePrice;
	}

	public void setStrikePrice(double strikePrice) {
		this.strikePrice = strikePrice;
	}

	public FnCalendar getMaturityDate() {
		return maturityDate;
	}

	public void setMaturityDate(FnCalendar maturityDate) {
		this.maturityDate = maturityDate;
	}

	public double getHisVol() {
		return hisVol;
	}

	public void setHisVol(double hisVol) {
		this.hisVol = hisVol;
	}
	
	public String getIrcId() {
		return ircId;
	}
	
	public double getMultiplier() {
		return multiplier;
	}

	public void setMultiplier(double multiplier) {
		this.multiplier = multiplier;
	}

	public void setIrcId(String ircId) {
		this.ircId = ircId;
	}

	public double getIrRate() {
		return irRate;
	}

	public void setIrRate(double irRate) {
		this.irRate = irRate;
	}

	public double getCurrentPrice() {
		return currentPrice;
	}

	public void setCurrentPrice(double currentPrice) {
		this.currentPrice = currentPrice;
	}

	public double getVolSpread() {
		Newton newton = new Newton();
//		Bisection newton = new Bisection();
		volSpread = newton.solvePrimeOp(this, 0.00000001, hisVol, -1.0, 2.0)- hisVol;
		return volSpread;
//		if(this.volSpread != 0.0){
//			return volSpread;
//		}
//		double dx, xMid, fxMid, fxMin , fxMax ,xMax,xMin,root, xAccuracy;
//		
//		xMax =1.0;
//		xMin =0.0;
//		fxMin =this.primeOp(xMin);
//		fxMax =this.primeOp(xMax);
//		xAccuracy  = 0.00000001;
//		int evaluationNumber  =0 ;
//		int maxEvaluationNumber =1000;
//		
//		 if (fxMin < 0.0) {
//	            dx = xMax - xMin;
//	            root = xMin;
//	        } else {
//	            dx = xMin - xMax;
//	            root = xMax;
//	        }
//
//	        while (evaluationNumber <= maxEvaluationNumber) {
//	            dx /= 2.0;
//	            xMid = root + dx;
//	            fxMid = this.primeOp(xMid);
//	            logger.debug("{},{}", fxMid, xMid);
//	            evaluationNumber++;
//	            if (fxMid <= 0.0){
//	            	root = xMid;
//	            }
//	            if (Math.abs(dx) < xAccuracy || fxMid == 0.0) {
//	            	volSpread = Math.round(root * 100000000)* 0.00000001 - getHisVol();
//	                return volSpread;
//	            }    
//	        }
//	        throw new ArithmeticException("maximum number of function evaluations exceeded"); // TODO: message
		
	}

	public void setVolSpread(double volSpread) {
		this.volSpread = volSpread;
	}
	public double getVol(){
		return this.getHisVol() + getVolSpread();
	}
	
	
	public double getPrice(){
		double rst =0.0;
		double d,d1, d2;
		CumulativeNormalDistribution norm = new CumulativeNormalDistribution();
		double tf = getMaturityDate().differDays(baseDate) /(double) 365 ;
		d = Math.log(getUnderlyingPrice()/getStrikePrice()) + (getIrRate()- getVol()*getVol()/2)*tf ;
		d1 = d /(getVol()* Math.sqrt(tf));
		d2 = d1 -getVol()*Math.sqrt(tf);
		
		if(isCall){
			rst =getUnderlyingPrice() * norm.primeOp(d1)
						- getStrikePrice() * Math.exp(-1* getIrRate()* tf) * norm.primeOp(d2);
		}else{
			rst = norm.primeOp(-1* d2) * getStrikePrice() * Math.exp(-1* getIrRate()* tf)
						- norm.primeOp(-1*d1)* getUnderlyingPrice();
		}
		return rst;
	}
	public double getPrice(double vol){
		double rst =0.0;
		double d,d1, d2;
		CumulativeNormalDistribution norm = new CumulativeNormalDistribution();
		double tf = getMaturityDate().differDays(baseDate) /(double) 365 ;
		d = Math.log(getUnderlyingPrice()/getStrikePrice()) + (getIrRate()- vol*vol/2)*tf ;
		d1 = d /(vol * Math.sqrt(tf));
		d2 = d1 - vol*Math.sqrt(tf);
		
		if(isCall){
			rst =getUnderlyingPrice() * norm.primeOp(d1)
						- getStrikePrice() * Math.exp(-1* getIrRate()* tf) * norm.primeOp(d2);
		}else{
			rst = norm.primeOp(-1* d2) * getStrikePrice() * Math.exp(-1* getIrRate()* tf)
						- norm.primeOp(-1*d1)* getUnderlyingPrice();
		}
		return rst;
	}
	
	public double getVega(){
		double rst;
		double d, d1;
		double tf = getMaturityDate().differDays(baseDate) /(double) 365 ;
		NormalDistribution norm = new NormalDistribution();
		
		d = Math.log(getUnderlyingPrice()/getStrikePrice()) + (getIrRate()- hisVol*hisVol/2)*tf ;
		d1 = d /(hisVol* Math.sqrt(tf));
		rst = getUnderlyingPrice() * norm.getPdf(d1)* Math.sqrt(tf);
		return rst;
	}
	public double getVega(double vol){
		double rst;
		double d, d1;
		double tf = getMaturityDate().differDays(baseDate) /(double) 365 ;
		NormalDistribution norm = new NormalDistribution();
		
		d = Math.log(getUnderlyingPrice()/getStrikePrice()) + (getIrRate()- vol*vol/2)*tf ;
		d1 = d /(vol* Math.sqrt(tf));
		rst = getUnderlyingPrice() * norm.getPdf(d1)* Math.sqrt(tf);
		return rst;
	}
	
	@Override
	public double derivative(double x){
		return getVega(x);
	}

	
	@Override
	public double derivative(FnCalendar baseDate, double x) {
		return 0;
	}

	@Override
	public double derivativeSecondOp(double x) {
		return 0;
	}

	@Override
	public double derivativeSecondOp(FnCalendar baseDate, double x) {
		return 0;
	}

	@Override
	public double primeOp(double x) {
		return getPrice(x)- getCurrentPrice();
	}

	@Override
	public double primeOp(FnCalendar baseDate, double x) {
		return 0;
	}

	@Override
	public double secondOp(FnCalendar baseDate, double x) {
		return 0;
	}
	
	@Override
	public boolean equals(Object other){
		return super.equals(other); 
	}
	@Override
	public int hashCode(){
		return super.hashCode();
	}

}
